/*
 * DAObject.java
 *
 * Created on March 22, 2007, 1:45 PM
 *
 */

package edu.byu.isys413.group1E.data;

import edu.byu.isys413.group1E.*;
import java.sql.*;
import java.util.*;

/** This class is to read and write BusinessObject information to and from the DB
 *
 * @author Travis Kulbeth
 */
public abstract class DAObject{
    /** Contains an instance of BackupDAO*/
    private static DAObject instance = null;
    

    /**Retrieves an instance of the TransactionDAO object.
     * This method is overridden in all subclasses, but is defined here to enable
     * the other classes getInstance methods to be accessed.
     *
     * @return instance
     */
    public static synchronized DAObject getInstance(){
        return instance;
    }    
    ///////////////////////////////////////////////////////////////////////////////
    /////////   CREATE
    /**
     * Creates a new BackupBO object with a newly generated GUID.
     * 
     * @return a newly created BackupBO with unique GUID
     * @throws java.lang.Exception to catch any errors in the creation process.
     */
    public abstract BusinessObject create() throws Exception;

    /**
     * This is a public read statement.  It loads an existing Backup record
     * from the database based on the RevSrcID and returns a BackupBO.
     * 
     * @return a BackupBO containing the information extracted from the requested
     * tuple in the DB.
     * @param id contains the unique identifier for the record to be read from the DB.
     * @throws edu.byu.isys413.group1E.DataException to catch any errors with the incoming data
     * @throws edu.byu.isys413.group1E.ConnectionPoolException to catch errors when connecting to the DB
     */
     public BusinessObject read(String id) throws SQLException, DataException, ConnectionPoolException   
     {
        Cache c = Cache.getInstance();
        BusinessObject bo = (BusinessObject)c.get(id);
        if (bo != null){
            return bo;                                                        // if so, return it immediately
        }
        
        //otherwise, get it from the database
        try {
            Connection conn = ConnectionPool.getInstance().get();             // retrieve a database connection from the pool
            bo = this.read(id, conn);                                              // call read with a connection
            conn.commit();
            ConnectionPool.getInstance().release(conn);                       // release the connection
        }catch (SQLException e) {
            //rollback
            throw new DataException("Could not retrieve record for Employee ID = " + id, e);
        }catch (ConnectionPoolException x) {
            //rollback
            throw new ConnectionPoolException("Could not establish connection to database.", x);
        }
        
        // release the connection back to the pool
        return bo;
     }
             
             

    /**
     * This is a public read statement that is called after a connection has been established with the DB.
     * It loads an existing Backup record from the database based on the RevSrcID.
     *
     *This class calls its super class, RevenueSourceDAO, to read in the information that is common across
     *all Revenue Sources, then retrieves the unique information.
     * 
     * @return BackupBO
     * @param id contains the given id for the Backup to be read
     * @param conn contains an already open connection to the DB
     * @throws edu.byu.isys413.group1E.ConnectionPoolException catches any errors with the connection
     * @throws edu.byu.isys413.group1E.DataException catches any errors with incoming data
     * @throws java.sql.SQLException catches any errors from the SQL execution
     */
    public abstract BusinessObject read(String id, Connection conn) throws SQLException, DataException;

    
    /**This is a public method to retrieve a list of all Backup items in the DB.
     * 
     * @throws edu.byu.isys413.group1E.DataException catches any errors with incoming data
     * @throws java.sql.SQLException catches any errors with SQL execution
     * @throws edu.byu.isys413.group1E.ConnectionPoolException catches any errors when connecting to the DB.
     * @return a list of all Backups from the DB.
     */
    public abstract ArrayList<BusinessObject> readAll() throws DataException, SQLException, ConnectionPoolException;
    
    /**
     * This method is really a part of save(bo, conn) in the super class.  It could be
     * embedded directly in it, but I've separated it into it's own method
     * to isolate the SQL udpate statement and make it more readable.  But
     * logically, it's really just a part of save.
     * 
     *This method is called by the save method in the super class after the common 
     *RS data has been saved to the DB.
     * 
     * @param bo contains the BackupBO to be updated to the DB
     * @param conn contains the connection already established with the DB
     * @throws edu.byu.isys413.group1E.ConnectionPoolException catches any errors with the connection
     * @throws edu.byu.isys413.group1E.DataException catches any errors with incoming data
     * @throws java.sql.SQLException catches any errors with SQL execution
     */
    /**
     * This is the public save method.  It is what is called when
     * the user (controller) code wants to save or update an object
     * into the database.
     */
    public synchronized void save(BusinessObject bo) throws DataException, ConnectionPoolException{
        
        try {
            Connection conn = ConnectionPool.getInstance().get();
            try {
                this.save(bo, conn);                                   // call save with a connection
                conn.commit();
            } catch (ConnectionPoolException ex) {
                conn.rollback();
                ex.printStackTrace();
                throw new SQLException();
            } catch (SQLException ex) {
                conn.rollback();
                ex.printStackTrace();
            } catch (DataException ex) {
                conn.rollback();
                ex.printStackTrace();
            }
            ConnectionPool.getInstance().release(conn);                       // release the connection
        }catch (SQLException e) {
            throw new DataException("Error saving record for id =" + bo.getId(), e);
        }catch (ConnectionPoolException x){
            throw new ConnectionPoolException("Could not establish connection to database.", x);
        }
        
        // release the connection back to the pool
        return;
    }
    
    /**
     *  This is a package method that is called by the public save (above) or
     *  by another DAO.  Either way we already have a connection to the database
     *  to use.  The user (controller) code never calls this one directly, since
     *  it can't know about Connection objects or SQLExceptions.
     *
     *  By having other DAOs call this save method (rather than update or
     *  insert below, each DAO in a chained save (like the RevenueSourceDAO calling
     *  MembershipDAO calling InterestDAO save chain) can independently decide
     *  whether to udpate or insert the BO it's saving.  That's why I made
     *  update and insert private rather than package level -- if you call
     *  them directly from another DAO, this DAO can't decide whether it's
     *  object needs to be inserted or updated.
     */
    public synchronized void save(BusinessObject bo, Connection conn) throws SQLException, DataException, ConnectionPoolException {
        // run update or insert
        if (bo.isIsDirty()) {
            if (bo.isAlreadyInDB()) {
                update(bo, conn);
            }else{
                insert(bo, conn);
            }
            // set the dirty flag to false now that we've saved it
            bo.setIsDirty(false);
        }

                
        // touch the cache for the object
        Cache c = Cache.getInstance();
        c.put(bo.getId(), bo);
    }
    
    public abstract void update(BusinessObject bo, Connection conn) throws SQLException, DataException;
    
    /**
     * This method is really a part of save(bo, conn) in the super class.  It could be
     * embedded directly in it, but I've separated it into it's own method
     * to isolate the SQL udpate statement and make it more readable.  But
     * logically, it's really just a part of save.
     * 
     *This method is called by the save method in the super class after the common 
     *RS data has been saved to the DB.
     * 
     * @param bo contains the BackupBO to be inserted into the DB
     * @param conn contains the connection already established with the DB
     * @throws edu.byu.isys413.group1E.ConnectionPoolException catches any errors with the connection
     * @throws edu.byu.isys413.group1E.DataException catches any errors with incoming data
     * @throws java.sql.SQLException catches any errors with SQL execution
     */
    public abstract void insert(BusinessObject bo, Connection conn) throws SQLException, DataException;
}
